﻿using System;
using System.Collections.Generic;
using System.Linq;
using Website.Apis;

namespace Website.App
{
    public class Game
    {
        public List<Player> Players { get; private set; }
        public List<int> AnPai { get; private set; }
        public List<int> MingPai { get; private set; }
        public Player CurrentPlayer { get; set; }
        public bool Started { get; set; }
        public Player Winner { get; set; }
        public Player Last { get; set; }
        public Guid Id { get; private set; }
        public DateTime StartTime { get; private set; }
        public string Name { get; set; }
        private int[] _lastPai;

        public Game()
        {
            Id = Guid.NewGuid();
            Players = new List<Player>();
            AnPai = new List<int>();
            MingPai = new List<int>();
        }

        public Player GetPlayer(long pid)
        {
            var p = Players.First(i => i.Id == pid);
            p.LogTime = DateTime.Now;
            return p;
        }

        private void AddCmd(string cmd, object data, Player player = null)
        {
            var json = new {cmd, data};

            if (player != null)
            {
                player.Commands.Enqueue(json);
            }
            else
            {
                foreach (var p in Players)
                {
                    p.Commands.Enqueue(json);
                }   
            }
        }

        public void AddPlayer(Player p)
        {
            if (Started)
                throw new Exception("不能中途加入。");
            AddCmd("add", p.Name);
            Players.Add(p);
            foreach (var item in Players)
            {
                AddCmd("add", item.Name, p);
            }
        }

        public long RestorePlayer(string name)
        {
            if (Started == false)
                throw new Exception("已结束。");
            return Players.First(i => i.Name == name).Id;
        }

        public void LeavePlayer(long pid)
        {
            if(Started)
                return;
            var p = Players.FirstOrDefault(i => i.Id == pid);
            if (p == null)
                return;
            Players.Remove(p);
            AddCmd("leave", p.Index);
        }

        public void Ready(long pid)
        {
            var p = Players.First(i => i.Id == pid);
            p.Ready = true;
            if (Players.Count < 2)
                return;
            if (Players.All(i => i.Ready))
            {
                Start();
            }
        }

        private void Start()
        {
            Started = true;
            StartTime = DateTime.Now;
            SetPlayerIndex();
            SetAnPai();
            FaPai(5);
            AddCmd("chupaier", Players[0].Index);
        }

        private void SetPlayerIndex()
        {
            if (Winner != null)
            {
                for (var i = 0; i < Players.Count; i++)
                {
                    var p = Players[0];
                    if (Winner.Id != p.Id)
                    {
                        Players.Remove(p);
                        Players.Add(p);
                    }
                    else
                    {
                        break;
                    }
                }
            }
            else
            {
                var p = Players[new Random().Next(0, Players.Count)];
                Players.Remove(p);
                Players.Insert(0, p);
            }
            for (var i = 0; i < Players.Count; i++)
            {
                Players[i].Index = i + 1;
                Players[i].ShowPai.Clear();
            }
            AddCmd("setpos", Players.Select(i => new {i.Index, i.Name}));
        }

        private void SetAnPai()
        {
            var anpai = new List<int>();
            for (var i = 0; i < Players.Count; i++)
            {
                anpai.AddRange(Rules.YiFuPai.ToArray());
            }
            AnPai = anpai.OrderBy(i => Guid.NewGuid()).ToList();
            MingPai.Clear();
        }

        private int NextAnPai()
        {
            if (AnPai.Count < 1)
            {
                AnPai = MingPai.OrderBy(i => Guid.NewGuid()).ToList();
                MingPai.Clear();
            }
            var pai = AnPai[0];
            AnPai.RemoveAt(0);
            return pai;
        }

        private void FaPai(int lun = 1)
        {
            for (var i = 0; i < lun; i++)
            {
                foreach (var p in Players)
                {
                    var pai = NextAnPai();
                    p.ShowPai.Add(pai);
                    AddCmd("fapai", pai, p);
                }
            }
            ShouPaiCount();
        }

        private Player NextPlayer()
        {
            if (CurrentPlayer.Index == Players.Count)
                return Players[0];
            return Players.First(i => i.Index == CurrentPlayer.Index + 1);
        }

        public void ChuPai(long pid, int[] pais)
        {
            if (Started == false)
                throw new Exception("游戏已结束。");
            if (pais.Length < 1)
                return;
            var p = Players.First(i => i.Id == pid);
            if (CurrentPlayer != null && p.Id != NextPlayer().Id)
                throw new Exception("正在等待" + NextPlayer().Name + "出牌。");
            if (CurrentPlayer == null && p.Id != Players[0].Id)
                throw new Exception("正在等待" + Players[0].Name + "出牌。");
            var back = p.ShowPai.ToList();
            foreach (var pai in pais)
            {
                for (var i = 0; i < back.Count; i++)
                {
                    if (back[i] == pai)
                    {
                        back.RemoveAt(i);
                        break;
                    }
                }
            }
            if (p.ShowPai.Count - pais.Length != back.Count)
            {
                throw new Exception("手牌验证失败。");
            }
            var result = _lastPai == null ? true : Rules.Check(_lastPai, pais);
            if (result.HasValue && result.Value == false)
            {
                throw new Exception("不符合规则。");
            }
            p.ShowPai.Clear();
            p.ShowPai.AddRange(back);
            AddCmd("chupai", new {p.Index, pais});
            if (p.ShowPai.Any() && result.HasValue)
            {
                CurrentPlayer = p;
                AddCmd("chupaier", NextPlayer().Index);
                _lastPai = pais;
            }
            else if (p.ShowPai.Any() && result.HasValue == false)
            {
                FaPai();
                AddCmd("chupaier", p.Index);
                _lastPai = null;
            }
            else
            {
                Started = false;
                Winner = p;
                foreach (var item in Players)
                {
                    item.Ready = false;
                }
                AddCmd("win",
                    new
                    {
                        p.Index,
                        data = Players.Where(i => i.ShowPai.Count > 0).Select(i => new {i.Index, i.ShowPai.Count})
                    });
            }
            MingPai.AddRange(pais);
            Last = p;
            ShouPaiCount();
        }

        public void Pass(long pid)
        {
            if (Last != null && pid == Last.Id)
                throw new Exception("必须出牌。");
            if (Last == null && pid == Players[0].Id)
                throw new Exception("必须出牌。");
            if (Last == null)
                return;
            var p = Players.First(i => i.Id == pid);
            var np = NextPlayer();
            if (p.Id != np.Id)
                throw new Exception("正在等待" + np.Name + "出牌。");
            CurrentPlayer = np;
            np = NextPlayer();
            AddCmd("pass", p.Index);
            if (np.Id == Last.Id)
            {
                _lastPai = null;
                FaPai();
            }
            AddCmd("chupaier", np.Index);
        }

        private void ShouPaiCount()
        {
            AddCmd("count", Players.Select(i => new {i.Index, i.ShowPai.Count}));
        }
    }
}